package com.lebang.component;


import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.Tuple;
import redis.clients.jedis.params.SetParams;

import javax.annotation.Resource;
import java.util.*;

@Component
@Slf4j
public class RedisClient {
    @Resource(name = "xianyu")
    private JedisPool jedisPool;

    private static final String LOCK_SUCCESS = "OK";

    private static final String SET_IF_NOT_EXIST = "NX";

    private static final String SET_WITH_EXPIRE_TIME = "PX";

    private static final Long RELEASE_SUCCESS = 1L;
    public void pub(String channel,String key) {

        try (Jedis jedis = jedisPool.getResource()) {
            jedis.publish(channel,key);
        } catch (Exception e) {
            log.error(e.getMessage());
        }
    }
    /**
     * <p>
     * 通过key获取所有的field和value
     * </p>
     *
     * @param key
     * @return
     */
    public Map<String, String> hgetall(String key) {
        Map<String, String> res = new HashMap<>();
        try (Jedis jedis = jedisPool.getResource()) {
            res = jedis.hgetAll(key);
        } catch (Exception e) {
            log.error(e.getMessage());
        }
        return res;
    }
    /**
     * <p>
     * 通过key获取储存在redis中的value
     * </p>
     * <p>
     * 并释放连接
     * </p>
     *
     * @param key
     * @return 成功返回value 失败返回null
     */
    public String get(String key) {

        String value = null;
        try (Jedis jedis = jedisPool.getResource()) {
            value = jedis.get(key);
           // log.info(value);
        } catch (Exception e) {
            log.error(e.getMessage());
        }
        return value;
    }



    /**
     * <p>
     * 向redis存入key和value,并释放连接资源
     * </p>
     * <p>
     * 如果key已经存在 则覆盖
     * </p>
     *
     * @param key
     * @param value
     * @return 成功 返回OK 失败返回 0
     */
    public String set(String key, String value) {

        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.set(key, value);
        } catch (Exception e) {

            log.error(e.getMessage());
            return "0";
        }
    }



    /**
     * <p>
     * 删除指定的key,也可以传入一个包含key的数组
     * </p>
     *
     * @param keys 一个key 也可以使 string 数组
     * @return 返回删除成功的个数
     */
    public Long del(String... keys) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.del(keys);
        } catch (Exception e) {

            log.error(e.getMessage());
            return 0L;
        }
    }
    public Long delOne(String  key) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.del(key);
        } catch (Exception e) {

            log.error(e.getMessage());
            return 0L;
        }
    }






    /**
     * <p>
     * 判断key是否存在
     * </p>
     *
     * @param key
     * @return true OR false
     */
    public Boolean exists(String key) {
          try (Jedis jedis = jedisPool.getResource()) {
            return jedis.exists(key);
        } catch (Exception e) {

            log.error(e.getMessage());
            return false;
        }
    }



    /**
     * <p>
     * 为给定 key 设置生存时间，当 key 过期时(生存时间为 0 )，它会被自动删除。
     * </p>
     *
     * @param key
     * @param value 过期时间，单位：秒
     * @return 成功返回1 如果存在 和 发生异常 返回 0
     */
    public Long expire(String key, int value) {
          try (Jedis jedis = jedisPool.getResource()) {
            return jedis.expire(key, value);
        } catch (Exception e) {
            log.error(e.getMessage());
            return 0L;
        }
    }

    /**
     * <p>
     * 以秒为单位，返回给定 key 的剩余生存时间
     * </p>
     *
     * @param key
     * @return 当 key 不存在时，返回 -2 。当 key 存在但没有设置剩余生存时间时，返回 -1 。否则，以秒为单位，返回 key
     * 的剩余生存时间。 发生异常 返回 0
     */
    public Long ttl(String key) {
          try (Jedis jedis = jedisPool.getResource()) {
            return jedis.ttl(key);
        } catch (Exception e) {

            log.error(e.getMessage());
            return 0L;
        }
    }



    /**
     * <p>
     * 新增key,并将 key 的生存时间 (以秒为单位)
     * </p>
     *
     * @param key
     * @param seconds 生存时间 单位：秒
     * @param value
     * @return 设置成功时返回 OK 。当 seconds 参数不合法时，返回一个错误。
     */
    public String setex(String key, int seconds, String value) {
          try (Jedis jedis = jedisPool.getResource()) {
            //log.info("插入执行记录：" + key + "  " + value);
            return jedis.setex(key, seconds, value);
        } catch (Exception e) {
            e.printStackTrace();
            log.error(e.getMessage());
        }
        return null;
    }




    /**
     * <p>
     * 设置key value并制定这个键值的有效期
     * </p>
     *
     * @param key
     * @param value
     * @param seconds 单位:秒
     * @return 成功返回OK 失败和异常返回null
     */
    public String setex(String key, String value, int seconds) {
        String res = null;
        try (Jedis jedis = jedisPool.getResource()) {
            res = jedis.setex(key, seconds, value);
        } catch (Exception e) {
            e.printStackTrace();
            log.error(e.getMessage());
        }
        return res;
    }









    /**
     * <p>
     * 对key的值做减减操作,如果key不存在,则设置key为-1
     * </p>
     *
     * @param key
     * @return
     */
    public Long decr(String key) {
        Long res = null;
        try (Jedis jedis = jedisPool.getResource()) {
            res = jedis.decr(key);
        } catch (Exception e) {
            e.printStackTrace();
            log.error(e.getMessage());
        }
        return res;
    }


    /**
     * <p>
     * 通过key给field设置指定的值,如果key不存在,则先创建
     * </p>
     *
     * @param key
     * @param field 字段
     * @param value
     * @return 如果存在返回0 异常返回null
     */
    public Long hset(String key, String field, String value) {
        Long res = null;
        try (Jedis jedis = jedisPool.getResource()) {
            res = jedis.hset(key, field, value);
        } catch (Exception e) {
            e.printStackTrace();
            log.error(e.getMessage());
        }
        return res;
    }





    /**
     * <p>
     * 通过key 和 field 获取指定的 value
     * </p>
     *
     * @param key
     * @param field
     * @return 没有返回null
     */
    public String hget(String key, String field) {
        String res = null;
        try (Jedis jedis = jedisPool.getResource()) {
            res = jedis.hget(key, field);
        } catch (Exception e) {
            e.printStackTrace();
            log.error(e.getMessage());
        }
        return res;
    }



    /**
     * <p>
     * 通过key给指定的field的value加上给定的值
     * </p>
     *
     * @param key
     * @param field
     * @param value
     * @return
     */
    public Long hincrby(String key, String field, Long value) {
        Long res = null;
        try (Jedis jedis = jedisPool.getResource()) {
            res = jedis.hincrBy(key, field, value);
        } catch (Exception e) {
            e.printStackTrace();
            log.error(e.getMessage());
        }
        return res;
    }
    public Long incrBy(String key, Long value) {
        Long res = null;
        try (Jedis jedis = jedisPool.getResource()) {
            res = jedis.incrBy(key,value);
        } catch (Exception e) {
            e.printStackTrace();
            log.error(e.getMessage());
        }
        return res;
    }
    /**
     * <p>
     * 通过key和field判断是否有指定的value存在
     * </p>
     *
     * @param key
     * @param field
     * @return
     */
    public Boolean hexists(String key, String field) {
        Boolean res = false;
        try (Jedis jedis = jedisPool.getResource()) {
            res = jedis.hexists(key, field);
        } catch (Exception e) {
            e.printStackTrace();
            log.error(e.getMessage());
        }
        return res;
    }

    /**
     * <p>
     * 通过key返回field的数量
     * </p>
     *
     * @param key
     * @return
     */
    public Long hlen(String key) {

        Long res = null;
        try (Jedis jedis = jedisPool.getResource()) {
            res = jedis.hlen(key);
        } catch (Exception e) {
            log.error(e.getMessage());
            res=0L;
        }
        return res;

    }

    /**
     * <p>
     * 通过key 删除指定的 field
     * </p>
     *
     * @param key
     * @param fields 可以是 一个 field 也可以是 一个数组
     * @return
     */
    public Long hdel(String key, String... fields) {

        Long res = null;
        try (Jedis jedis = jedisPool.getResource()) {
            res = jedis.hdel(key, fields);
        } catch (Exception e) {
            e.printStackTrace();
            log.error(e.getMessage());
        }
        return res;
    }



    /**
     * <p>
     * 通过key向list头部添加字符串
     * </p>
     *
     * @param key
     * @param strs 可以使一个string 也可以使string数组
     * @return 返回list的value个数
     */
    public Long lpush(String key, String... strs) {

        Long res = null;
        try (Jedis jedis = jedisPool.getResource()) {
            res = jedis.lpush(key, strs);
        } catch (Exception e) {
            e.printStackTrace();
            log.error(e.getMessage());
        }
        return res;
    }



    /**
     * <p>
     * 通过key从list尾部删除一个value,并返回该元素
     * </p>
     *
     * @param key
     * @return
     */
     public String rpop(String key) {

        String res = null;
         try (Jedis jedis = jedisPool.getResource()) {
            res = jedis.rpop(key);
        } catch (Exception e) {
            e.printStackTrace();
            log.error(e.getMessage());
        }
        return res;
    }

    /**
     * <p>
     * 通过key返回list的长度
     * </p>
     *
     * @param key
     * @return
     */
    public Long llen(String key) {

        Long res = null;
        try (Jedis jedis = jedisPool.getResource()) {
            res = jedis.llen(key);
        } catch (Exception e) {
            log.error(e.getMessage());
            return 0L;
        }
        return res;
    }





    /**
     * <p>
     * 通过key向zset中添加value,score,其中score就是用来排序的
     * </p>
     * <p>
     * 如果该value已经存在则根据score更新元素
     * </p>
     *
     * @param key
     * @param score
     * @param member
     * @return
     */
    public Long zadd(String key, double score, String member) {

        Long res = null;
          try (Jedis jedis = jedisPool.getResource()) {
            res = jedis.zadd(key, score, member);
        } catch (Exception e) {
            e.printStackTrace();
            log.error(e.getMessage());
        }
        return res;
    }

    /**
     * <p>
     * 返回有序集 key 中，指定区间内的成员。min=0,max=-1代表所有元素
     * </p>
     *
     * @param key
     * @param min
     * @param max
     * @return 指定区间内的有序集成员的列表。
     */
    public Set<String> zrange(String key, long min, long max) {
          try (Jedis jedis = jedisPool.getResource()) {
            return jedis.zrange(key, min, max);
        } catch (Exception e) {
            log.error(e.getMessage());
        }
        return new HashSet<>();
    }

    /**
     * <p>
     * 返回倒序集 key 中，指定区间内的成员。min=0,max=-1代表所有元素
     * </p>
     *
     * @param key
     * @param min
     * @param max
     * @return 指定区间内的有序集成员以及分数。
     */
    public Set<String> zrevrangeWithCount(String key, long min, long max) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.zrevrangeByScore(key, min, max);
        } catch (Exception e) {
            log.error(e.getMessage());
        }
        return new HashSet<>();
    }

    /**
     * <p>
     * 返回倒序集 key 中，指定区间内的成员。min=0,max=-1代表所有元素
     * </p>
     *
     * @param key
     * @param min
     * @param max
     * @return 指定区间内的有序集成员以及分数。
     */
    public Set<Tuple> zrevrangeWithScores(String key, long min, long max) {
          try (Jedis jedis = jedisPool.getResource()) {
            return jedis.zrevrangeWithScores(key, min, max);
        } catch (Exception e) {
            log.error(e.getMessage());
        }
        return new HashSet<>();
    }


    /**
     * <p>
     * 为哈希表 key 中的域 field 的值加上增量 increment 。增量也可以为负数，相当于对给定域进行减法操作。 如果 key
     * 不存在，一个新的哈希表被创建并执行 HINCRBY 命令。如果域 field 不存在，那么在执行命令前，域的值被初始化为 0 。
     * 对一个储存字符串值的域 field 执行 HINCRBY 命令将造成一个错误。本操作的值被限制在 64 位(bit)有符号数字表示之内。
     * </p>
     * <p>
     * 将名称为key的hash中field的value增加integer
     * </p>
     *
     * @param key
     * @param value
     * @param increment
     * @return 执行 HINCRBY 命令之后，哈希表 key 中域 field的值。异常返回0
     */
    public Long hincrBy(String key, String value, long increment) {
          try (Jedis jedis = jedisPool.getResource()) {
            return jedis.hincrBy(key, value, increment);
        } catch (Exception e) {
            log.error(e.getMessage());
            return 0L;
        }

    }


    /**
     * <p>
     * 通过key返回zset中value的排名
     * </p>
     * <p>
     * 下标从大到小排序
     * </p>
     *
     * @param key
     * @param member
     * @return
     */
    public Long zrevrank(String key, String member) {

        Long res = null;
        try (Jedis jedis = jedisPool.getResource()) {
            res = jedis.zrevrank(key, member);
        } catch (Exception e) {
            log.error(e.getMessage());
        }
        return res;
    }



    /**
     * <p>
     * 通过key返回zset中的value个数
     * </p>
     *
     * @param key
     * @return
     */
    public Long zcard(String key) {

        Long res = null;
        try (Jedis jedis = jedisPool.getResource()) {
            res = jedis.zcard(key);
        } catch (Exception e) {
            e.printStackTrace();
            log.error(e.getMessage());
        }
        return res;
    }

    /**
     * <p>
     * 通过key获取zset中value的score值
     * </p>
     *
     * @param key
     * @param member
     * @return
     */
    public Double zscore(String key, String member) {

        Double res = null;
        try (Jedis jedis = jedisPool.getResource()) {
            res = jedis.zscore(key, member);
        } catch (Exception e) {
            e.printStackTrace();
            log.error(e.getMessage());
        }
        return res;
    }


    /**
     * <p>
     * 返回满足pattern表达式的所有key
     * </p>
     * <p>
     * keys(*)
     * </p>
     * <p>
     * 返回所有的key
     * </p>
     *
     * @param pattern
     * @return
     */
    public Set<String> keys(String pattern) {

        Set<String> res = null;
        try (Jedis jedis = jedisPool.getResource()) {
            res = jedis.keys(pattern);
        } catch (Exception e) {
            log.error(e.getMessage());
        }
        return res;
    }



    /**
     * 返还到连接池
     *
     * @param jedis
     */
    private static void returnResource(Jedis jedis) {
        if (jedis != null) {
            jedis.close();
        }
    }

    /**
     * 尝试获取分布式锁
     * jedis Redis客户端
     *
     * @param lockKey    锁
     * @param requestId  加锁密码
     * @param expireTime 锁自动解开时间,毫秒
     * @return 是否获取成功
     */
    public boolean tryGetDistributedLock(String lockKey, String requestId, int expireTime) {
        /*
         *设置锁并设置超时时间，lockKey表示Redis key，requestId表示Redis value，SET_IF_NOT_EXIST表示有值不进行设置（NX），
         * SET_WITH_EXPIRE_TIME表示是否设置超时时间（PX）设置，expireTime表示设置超时的毫秒值
         * */
          try (Jedis jedis = jedisPool.getResource()) {
            //String result = jedis.set(lockKey, requestId, "NX", "PX", expireTime);
             SetParams setParams=new SetParams().nx().px(expireTime);
              String result = jedis.set(lockKey, requestId, setParams);
            return LOCK_SUCCESS.equals(result);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 尝试获取分布式锁
     * jedis Redis客户端
     *
     * @param lockKey    锁
     * @param requestId  加锁密码
     * @param expireTime 锁自动解开时间,毫秒
     * @param waitTime   最大等待时间
     * @return 是否获取成功
     */
    public boolean tryGetDistributedLock(String lockKey, String requestId, int expireTime, int waitTime) {
        /*
         *设置锁并设置超时时间，lockKey表示Redis key，requestId表示Redis value，SET_IF_NOT_EXIST表示有值不进行设置（NX），
         * SET_WITH_EXPIRE_TIME表示是否设置超时时间（PX）设置，expireTime表示设置超时的毫秒值
         * */
        long maxTime = System.currentTimeMillis() + waitTime;
        try (Jedis jedis = jedisPool.getResource()) {
            while (true) {
                long now = System.currentTimeMillis();
                if (now > maxTime) {
                    return false;
                }

                //String result = jedis.set(lockKey, requestId, "NX", "PX", expireTime);
                SetParams setParams=new SetParams().nx().px(expireTime);
                String result = jedis.set(lockKey, requestId, setParams);
                if (LOCK_SUCCESS.equals(result)) {
                    return true;
                }
                Thread.sleep(1000);
            }
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 释放分布式锁
     * Redis客户端
     *
     * @param lockKey   锁
     * @param requestId 解锁密码
     * @return 是否释放成功
     */
    public boolean releaseDistributedLock(String lockKey, String requestId) {

        /*
         * 利用Lua脚本代码，首先获取锁对应的value值，检查是否与requestId相等，如果相等则删除锁（解锁）
         * eval命令执行Lua代码的时候，Lua代码将被当成一个命令去执行，并且直到eval命令执行完成，Redis才会执行其他命令，这样就不会出现上一个代码执行完挂了后边的出现问题，还是一致性的解决
         * */
          try (Jedis jedis = jedisPool.getResource()) {
            String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
            Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));
            return RELEASE_SUCCESS.equals(result);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /*** <p>Description: 该key还能存活多久 </p>
     * @author wenquan
     * @date 2017年1月5日
     * @param key
     */

    public Long timetolive(String key) {
        Long res = 0L;
        try (Jedis jedis = jedisPool.getResource()) {
            res = jedis.ttl(key);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return res;
    }

    /**
     * <p>
     * 通过key 和 field 获取指定的 value
     * </p>
     *
     * @param key
     * @param field
     * @return 没有返回null
     */
    public Optional<String> Optionalhget(String key, String field) {
        String res = null;
        try (Jedis jedis = jedisPool.getResource()) {
            res = jedis.hget(key, field);
        } catch (Exception e) {
            e.printStackTrace();
            log.error(e.getMessage());
        }
        return Optional.ofNullable(res);
    }

    public Jedis getJedis() {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis;
        }
    }

    public Map<String, Object> testInfo() {

        Map<String, Object> map = new HashMap<>();

        map.put("numWaiters", jedisPool.getNumWaiters());
        map.put("MaxBorrowWaitTimeMillis", jedisPool.getMaxBorrowWaitTimeMillis());
        map.put("MeanBorrowWaitTimeMillis", jedisPool.getMeanBorrowWaitTimeMillis());
        map.put("NumActive", jedisPool.getNumActive());
        map.put("NumIdle", jedisPool.getNumIdle());
        return map;
    }
    public int getNumActive() {

      return jedisPool.getNumActive();
    }
}
